/*
 * @(#)PagerTag.java $version 2010. 8. 11
 *
 * Copyright 2007-2010 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nhncorp.usf.web.taglibs.pager;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;

import org.apache.commons.lang.StringUtils;

import com.nhncorp.lucy.common.data.DataHandler;
import com.nhncorp.lucy.common.data.DataHandlerFactory;
import com.nhncorp.lucy.common.message.MessageUtil;
import com.nhncorp.usf.core.result.template.directive.PagerInfo;
import com.nhncorp.usf.web.taglibs.TagUtils;

/**
 * 링크 정보를 생성하기 위한 {@link TagSupport}.
 *
 * @author Web Platform Development Team
 */
public abstract class PagerTag extends TagSupport {
	private static final long serialVersionUID = 3366791799259050159L;

	protected static final String END_ATAG = "</a>";
	protected static final String DEFAULT_PAGE_NAME = "page";
	protected static final String DEFAULT_TOTALROWS_NAME = "totalRows";
	protected static final Pattern PATTERN = Pattern.compile("\\$\\{[^\\}]*\\}"); // share

	protected String[] appendings;
	protected String pageName = DEFAULT_PAGE_NAME;
	protected String totalRowsName = DEFAULT_TOTALROWS_NAME;

	public PagerTag() {
	}

	public PagerTag(String... appendings) {
		this.appendings = appendings;
	}

	public void setPageName(String pageName) {
		this.pageName = pageName;
	}

	public void setTotalRowsName(String totalRowsName) {
		this.totalRowsName = totalRowsName;
	}

	public void setAppendings(String appendings) {
		this.appendings = appendings.split(",");
	}

	/**
	 * @throws JspException JspException
	 * @return int
	 * */
	@Override
	public int doStartTag() throws JspException {
		NavigatorTag parent = (NavigatorTag)getParent();
		HttpServletRequest req = (HttpServletRequest)pageContext.getRequest();
		PagerInfo pagerInfo = (PagerInfo)req.getAttribute("pagerInfo");

		try {
			generate(parent, pagerInfo);
		} catch (Exception e) {
			failed(e);
		}
		return EVAL_PAGE;
	}

	/**
	 * @param parent {@link NavigatorTag}
	 * @param pagerInfo {@link PagerInfo}
	 * @throws Exception Exception
	 * */
	protected abstract void generate(NavigatorTag parent, PagerInfo pagerInfo) throws Exception;

	/**
	 * @param ex {@link Exception}
	 * */
	protected abstract void failed(Exception ex);

	/**
	 * @param queryString queryString
	 * @param page page
	 * @param totalRows totalRows
	 * @param appendTotalRows appendTotalRows
	 * @return queryString
	 * */
	protected String appendPageParameter(String queryString, int page, int totalRows, boolean appendTotalRows) {
		if (queryString == null) {
			queryString = pageName + "=" + page;
		}
		if (queryString.startsWith(pageName + "=")) {
			queryString = queryString.replaceFirst(pageName + "=[0-9]*", pageName + "=" + page);
		} else if (queryString.indexOf("&" + pageName + "=") < 0) {
			queryString += "&" + pageName + "=" + page;
		}
		if (appendTotalRows && queryString.indexOf("&" + totalRowsName + "=") < 0 && totalRows >= 0) {
			queryString += "&" + totalRowsName + "=" + totalRows;
		}

		return queryString.replaceFirst("\\&" + pageName + "=[0-9]*", "&" + pageName + "=" + page);
	}

	/**
	 * @param context {@link PageContext}
	 * @return requestURL
	 * */
	protected String getRequestURL(PageContext context) {
		String result = (String)context.getAttribute("requestURI");
		
		if (result.endsWith("?")) {
			result = result.substring(0, result.length() - 1);
		}

		return result;
	}

	/**
	 * @param pagerInfo {@link PagerInfo}
	 * @return queryString
	 * */
	protected String getQueryString(PagerInfo pagerInfo) {
		String queryString = (String)pageContext.getAttribute("queryString");

		if (queryString == null) {
			queryString = pagerInfo.getQueryString();
		}

		return queryString;
	}

	/**
	 * @param parent {@link NavigatorTag}
	 * @param query query
	 * @param index index
	 * @param atagAttribute atagAttribute
	 * @return startTag
	 * */
	protected String getStartTag(NavigatorTag parent, String query, int index, String atagAttribute) {
		StringBuilder builder = new StringBuilder(64);
		builder.append("<a href=\"");
		builder.append(TagUtils.replaceSymbolToEntity(getRequestURL(pageContext)));
		builder.append('?');
		builder.append(appendPageParameter(query, index, parent.totalRows, parent.appendTotalRows));

		builder.append('\"');
		
		if (!StringUtils.isEmpty(atagAttribute)) {
			builder.append(' ').append(atagAttribute);
		}
		
		if (!StringUtils.isEmpty(parent.getAttributes())) {
			builder.append(' ').append(parent.getAttributes());
		}
		
		if (appendings != null && appendings.length > 0) {
			for (String appending : appendings) {
				builder.append(' ').append(appending);
			}
		}
		
		builder.append('>');
		return builder.toString();
	}

	/**
	 * @param target target
	 * @param handlerName handlerName
	 * @return String
	 * */
	protected String resolve(String target, String handlerName) {
		if (handlerName == null) {
			return MessageUtil.getCompleleteMessage(target); // from message resource
		}
		
		DataHandler handler = DataHandlerFactory.getDataHandler(handlerName);
		
		if (handler == null || !target.contains("$")) {
			return target;
		}
		
		Matcher matcher = PATTERN.matcher(target);
		StringBuffer sb = new StringBuffer();
		
		while (matcher.find()) {
			String matched = matcher.group();
			
			if (matched == null) {
				matched = ""; // for Klocwork
			}
			
			String reference = matched.substring(2, matched.length() - 1); // ${...}
			matcher.appendReplacement(sb, handler.get(reference)); // throws DataHandlerException
		}

		matcher.appendTail(sb);
		return sb.toString();
	}
}